Azure cli 通过复制的vhd重建虚机

说明

本文适合跨账户迁移虚机。

原理通过公开的sas url 复制磁盘镜像到存储账户里,然后再从存储账户的vhd文件创建托管磁盘,再利用托管磁盘创建虚机

需要注意的是:虚机加载的额外磁盘需要卸掉,或者在fstab 磁盘加载项添加 nofail, 如 centos配置:

UUID=1037feaf-5034-446c-bbdc-e1958be6b35a /data  xfs     defaults,nofail,nodev,nouser,noexec,nosuid,noatime,nodiratime 0 0

如果没修改,可以在已启动的虚机里加载 vhd文件 或 托管磁盘,修改fstab后在创建虚机

参考:
https://docs.microsoft.com/zh-cn/cli/azure/reference-index?view=azure-cli-latest

Azure 登陆

## 切换到中国云
az cloud set -n AzureChinaCloud

## 登陆
az login

## 浏览器打开,先用账户登陆Azure portal,然后在新tab page里打开
## https://microsoft.com/deviceloginchina

## 如果需要设置订阅
az account set --subscription $s_SubscriptionId

用到的变量

## t_ 表示target_, 代表目标资源
## s_ 表示source_, 代表源资源



##--源资源变量------------------------------------------------------

## 源订阅
s_SubscriptionId=ddrt394e-0463-4eta5-8d04-c94wetfd879dc

## 源区域
s_Location="chinaeast"

## 源资源组
s_ResourceGroupName="server_rg"


## 源托管磁盘镜像名
s_SnapshotName="server01_OsDisk-hdd201801"



##--目标资源变量------------------------------------------------------

## 目标订阅
t_SubscriptionId=653gfb1f7-f219-4sgfeb-eret-31wrtwe1efb0

## 目标区域, chinanorth chinaeast
t_Location="chinaeast"

## 目标资源组
t_ResourceGroupName="test_rg"



##--目标存储账户变量------------------------------------------------------

## 目标存储账户名
t_StorageAccountName="serverstg01"

## 目标存储账户容器名
t_StorageContainerName="data"

## 存储账户访问密匙,这里随便几个字符表示一下
t_StorageAccountKey="aA8bfS4CnG0t7tW+QudkMYYt+IBbp6ha7iPzo9mNFDGp7M4u2UBw=="


##--目标网络资源变量------------------------------------------------------

## 高可用集名
t_HASetName="HASet"

## 安全组名
t_NSGName="test_ng"


## 公网IP
t_PIPName1="test01_IP1"
t_PIPName2="test02_IP1"

## 虚拟网卡名
t_NICName1="test01_nic1"
t_NICName2="test02_nic1"



## 虚拟网络名
t_VnetName="test01-vnet-101e"

## 虚拟网络地址空间
t_VnetAddressPrefix="10.10.10.0/24 10.10.11.0/24 10.10.12.0/24"



## 子网1名
t_SubnetName1="test01-snet-101e"
## 子网1地址
t_SubnetNamePrefix1="10.10.10.0/24"

## 子网2名和子网2地址
t_SubnetName2="test_app_gatewy"
t_SubnetNamePrefix2="10.10.11.0/24"


##--新建资源变量------------------------------------------------------

## 导出时间限制,过期时间,3600 秒 = 1 小时
## 创建的sas url 连接时,设置能够访问的时间,就是说从创建开始到不能从这个sas url下载磁盘镜像的时间长度
sasExpiryDuration=3600

## 目标 VHD 文件保存名
t_VHDFileName="osdisk.vhd"


##--新建托管磁盘变量------------------------------------------------------

## 新建的托管磁盘名
t_ManagedDiskName="test01_OsDisk_1"

## 新建虚机存放格式,这里用ssd, Premium_LRS or Standard_LRS
t_Sku="Premium_LRS"

## 新建磁盘大小,单位GB
t_DiskSize=32


##--新建虚机变量------------------------------------------------------

## 新建虚机名
t_VMName1="test01"

## 新建虚机类型
t_OS_Type=linux

## 新建虚机大小
t_Size="Standard_DS2_v2"

创建资源组

## 查询资源组里的资源,不带--resource-group查询订阅的所有资源
az resource list --resource-group $t_ResourceGroupName --output table

## 创建资源组

az group create \
--name $t_ResourceGroupName \
--location $t_Location

创建安全组

az network nsg create \
--location $t_Location \
--resource-group $t_ResourceGroupName \
--name $t_NSGName

开启端口3890

az network nsg rule create \
--location $t_Location \
--resource-group $t_ResourceGroupName \
--nsg-name $t_NSGName \
--name RDP \
--protocol tcp \
--direction inbound \
--source-address-prefix '*' \
--source-port-range '*'  \
--destination-address-prefix '*' \
--destination-port-range 3890 \
--access allow \
--priority 1100

开启端口22

az network nsg rule create \
--location $t_Location \
--resource-group $t_ResourceGroupName \
--nsg-name $t_NSGName \
--name SSH \
--protocol tcp \
--direction inbound \
--source-address-prefix '*' \
--source-port-range '*'  \
--destination-address-prefix '*' \
--destination-port-range 22 \
--access allow \
--priority 1100

开启80端口

az network nsg rule create \
--location $t_Location \
--resource-group $t_ResourceGroupName \
--nsg-name $t_NSGName \
--name HTTP \
--protocol tcp \
--direction inbound \
--source-address-prefix '*' \
--source-port-range '*'  \
--destination-address-prefix '*' \
--destination-port-range 80 \
--access allow \
--priority 1200

创建可用集

az vm availability-set create \
--name $t_HASetName \
--resource-group $t_ResourceGroupName \
--platform-fault-domain-count 2 \
--platform-update-domain-count 5

创建虚拟网络和子网

az network vnet create \
--resource-group $t_ResourceGroupName \
--name $t_VnetName \
--address-prefix $t_VnetAddressPrefix \
--subnet-name $t_SubnetName1 \
--subnet-prefix $t_SubnetNamePrefix1

## 其它参数:DDOS防护,启用的是标准防护,需要收费,默认基础防护不需要设置。
[--ddos-protection {false, true}]
[--ddos-protection-plan]
[--vm-protection {false, true}]

创建子网

az network vnet subnet create \
--resource-group $t_ResourceGroupName \
--vnet-name $t_VnetName \
--name  t_SubnetName2 \
--address-prefix t_SubnetNamePrefix2 \
--network-security-group $t_NSGName

## 其它参数:
--route-table MyRouteTable
[--service-endpoints]

创建存储帐户

az storage account create \
--resource-group $t_ResourceGroupName \
--location $t_Location \
--name $t_StorageAccountName \
--kind Storage \
--sku Standard_LRS

创建存储容器

az storage container create \
--account-name $t_StorageAccountName \
--name $t_StorageContainerName

查询存储第一个access key

storageAccountKey=$(az storage account keys list \
--account-name $t_StorageAccountName \
--resource-group $t_ResourceGroupName \
--query [0].value --output tsv)

托管磁盘转vhd

## 登陆到源订阅上操作

## 从托管磁盘镜像创建sas url,通过此连接可用于外网用户直接访问下载镜像
## 也可以直接从portal界面创建
s_SAS=$(az snapshot grant-access --resource-group $s_ResourceGroupName --name $s_SnapshotName --duration-in-seconds $sasExpiryDuration --query [accessSas] -o tsv)

## 此连接将会是这样一个url
## echo $s_SAS
## https://md-jzj4xh1pwpkc.blob.core.chinacloudapi.cn/d1dcd5z4mb2g/abcd?sv=2017-04-17&sr=b&si=4484c9bb-e110-424c-aae8-169cca49c4af&sig=ARBeP4FNuhlphRB03a97c71HXQ9P8Xd1IRu9Eb0x8zU%3D



## 登陆到目标订阅上操作

## 将sar url 复制下来,并复制给变量

s_SAS="https://md-jzj4xh1pwpkc.blob.core.chinacloudapi.cn/d1dcd5z4mb2g/abcd?sv=2017-04-17&sr=b&si=4484c9bb-e110-424c-aae8-169cca49c4af&sig=ARBeP4FNuhlphRB03a97c71HXQ9P8Xd1IRu9Eb0x8zU%3D"

## 通过异步复制,将$s_SAS 的磁盘镜像复制到存储账户里,存为一个vhd文件
az account set --subscription $t_SubscriptionId

az storage blob copy start \
--destination-blob $t_VHDFileName \
--destination-container $t_StorageContainerName \
--account-name $t_StorageAccountName \
--account-key $t_StorageAccountKey \
--source-uri $s_SAS

vhd转托管磁盘

## 存储帐户中从 VHD 文件创建托管磁盘,如果未设置大小,默认和源镜像一样大
az account set --subscription $t_SubscriptionId

## 获取存储账户密匙
storageAccountKey=$(az storage account keys list \
--account-name $t_StorageAccountName \
--resource-group $t_ResourceGroupName \
--query [0].value --output tsv)

## 获取blob url
vhd_url=$(az storage blob url  \
--name $t_VHDFileName \
--account-key $storageAccountKey \
--account-name $t_StorageAccountName \
--container-name $t_StorageContainerName)

## 查看vhd_url内容
## echo $vhd_url
## https://testserverdiag01.blob.core.chinacloudapi.cn/data/vm_os.vhd


az disk create \
--resource-group $t_ResourceGroupName \
--name $t_ManagedDiskName \
--location $t_Location \
--sku $t_Sku \
--size-gb $t_DiskSize \
--source $vhd_url

从托管磁盘镜像创建托管磁盘

az account set --subscription $t_SubscriptionId

## 获取托管磁盘镜像ID
snapshotId=$(az snapshot show --name $s_SnapshotName --resource-group $s_ResourceGroupName --query [id] -o tsv)

#通过镜像ID创建托管磁盘,如果未设置大小,默认和源镜像一样大
az disk create \
--resource-group $t_ResourceGroupName \
--name $t_ManagedDiskName \
--sku $t_Sku \
--size-gb $t_DiskSize \
--source $snapshotId

使用现有托管 OS 磁盘创建虚拟机

创建对外IP

az network public-ip create \
--name $t_PIPName1 \
--resource-group $t_ResourceGroupName \
--location $t_Location \
--allocation-method Static

## 其它参数:
[--dns-name]                :--dns-name Mydnsname
[--idle-timeout]
[--ip-tags]
[--reverse-fqdn]
[--sku {Basic, Standard}]
[--tags]
[--version {IPv4, IPv6}]
[--zone {1, 2, 3}]          : --zone 2

创建网卡(虚拟 NIC)

az network nic create \
--name $t_NICName1 \
--location $t_Location \
--resource-group $t_ResourceGroupName \
--vnet-name $t_VnetName \
--subnet $t_SubnetName \
--network-security-group $t_NSGName \
--public-ip-address $t_PIPName1

## 如果虚拟网络不再同一个资源组里,必须指定子网id,而不是子网名和虚拟网络名
## --subnet "/subscriptions/91913983-c6cc-4aea-a712-87dd97b5c85b/resourceGroups/tada-web-rg01e/providers/Microsoft.Network/virtualNetworks/tada-web-vnet-101e/subnets/tada-web-snet-102e"

获取托管磁盘ID

managedDiskId=$(az disk show --name $t_ManagedDiskName --resource-group $t_ResourceGroupName --query [id] -o tsv)

从托管磁盘中创建vm

az vm create --name $t_VMName1 \
--resource-group $t_ResourceGroupName \
--nics $t_NICName1 \
--attach-os-disk $managedDiskId \
--os-type $t_OS_Type \
--size $t_Size \
--availability-set $t_HASetName \
--os-disk-caching ReadOnly

## 多块网卡只需空格隔开,如:--nics $t_NICName11 $t_NICName11

上传本地vhd创建虚机

## 获取存储账户密匙
storageAccountKey=$(az storage account keys list \
--account-name $t_StorageAccountName \
--resource-group $t_ResourceGroupName \
--query [0].value --output tsv)


az storage blob upload 
--account-name $t_StorageAccountName \
--container-name $t_StorageContainerName \
--account-key $storageAccountKey \
--type page \
--file /path/to/disk/mydisk.vhd \
--name $t_VHDFileName


az disk create \
    --resource-group $t_ResourceGroupName \
    --name $t_ManagedDiskName \
    --sku $t_Sku \
    --size-gb $t_DiskSize \
    --source https://mystorageaccount.blob.core.chinacloudapi.cn/mydisks/myDisk.vhd

从自定义映像创建虚机

## 用vhd或托管磁盘创建虚机映像

az image create  --os-type Windows -g $t_ResourceGroupName -n image1 --source myManagedDisk

az image create  --os-type Windows -g $t_ResourceGroupName -n image1 --source https://mystorageaccount.blob.core.chinacloudapi.cn/mydisks/myDisk.vhd

az image list --resource-group $t_ResourceGroupName

az image show --name
            --resource-group
            [--expand]



## 查看可用虚机大小
az vm list-sizes --location $t_Location --output table
az vm list-skus --location $t_Location --output table

## 查看IP地址
az vm list-ip-addresses [--name] --resource-group $t_ResourceGroupName --output table

## 查看自定义虚机映像
az image list --resource-group $t_ResourceGroupName


az vm create \
--name $t_VMName1 \
--resource-group $t_ResourceGroupName \
--location $t_Location \
--image mycustomimage \
--vnet-name $t_VnetName \
--subnet $t_SubnetName1 \
--nics t_NICName1\
--availability-set $t_HASetName \
--os-type windows \
--os-disk-caching ReadOnly \
--storage-sku Premium_LRS \
--size Standard_DS2_v2 \
--os-disk-name $t_VMName1"_os" \
--private-ip-address 10.10.0.4/24 \


## 利用模板创建
https://github.com/Azure/azure-quickstart-templates/tree/master/201-vm-specialized-vhd

托管磁盘复制

## 最好先停止虚机或卸载托管磁盘,否则,如果磁盘正在写入数据,无法保证数据一致性,导致某些文件破坏或程序用不了。

## 获取托管磁盘ID

    managedDiskId=$(az disk show --name $t_ManagedDiskName --resource-group $s_ResourceGroupName --query [id] -o tsv)

##  使用托管磁盘ID复制托管磁盘到不同的订阅或组

az account set --subscription $t_SubscriptionId
az disk create --resource-group $t_ResourceGroupName --name $t_ManagedDiskName --source $managedDiskId

托管磁盘直接导出

## 参考:https://docs.azure.cn/zh-cn/articles/azure-operations-guide/virtual-machines/aog-virtual-machines-howto-export-managed-disks

## 创建导出http url连接,相当于图像界面的export导出磁盘连接
## 如果是镜像,只需把az disk 换成 az snapshot

sas=$(az disk grant-access \
        --duration-in-seconds 3600 \
        --name $t_ManagedDiskName \
        --resource-group $s_ResourceGroupName \
        --query [accessSas] -o tsv)

## 复制

az storage blob copy start \
        --destination-blob $t_VHDFileName \
        --destination-container $t_StorageContainerName \
        --account-name $t_StorageAccountName \
        --account-key $storageAccountKey \
        --source-uri $sas

导出blob

## 可以外网直接下载访问

存储参考:

https://azurecitadel.github.io/guides/cli/cli-4-bash/
blob=azuredeploy.json
container=templates
expiry=$(date '+%Y-%m-%dT%H:%MZ' --date "+30 minutes")

export accountName=$(az storage account list --resource-group ExampleResourceGroup --query [0].name --output tsv)
storageAccountKey=$(az storage account keys list --account-name $accountName --resource-group ExampleResourceGroup --query [0].value --output tsv)
sasToken=$(az storage blob generate-sas --account-name $accountName --account-key $storageAccountKey --container-name templates --name azuredeploy.json --permissions r --expiry $expiry --output tsv)
shortURL=$(az storage blob url --container-name $container --name $blob) 
fullURL=$shortURL?$sasToken
echo $fullURL